package xyz.noark.codec.along;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONB;
import com.alibaba.fastjson2.JSONFactory;
import com.alibaba.fastjson2.JSONWriter;
import com.github.houbb.data.factory.core.util.DataUtil;
import io.protostuff.runtime.RuntimeSchema;
import org.apache.fury.Fury;
import org.apache.fury.config.Language;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.results.format.ResultFormatType;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import xyz.noark.codec.along.data.HarmDTO;
import xyz.noark.codec.along.data.SerializingUtil;
import xyz.noark.codec.along.data.SkillCategory;
import xyz.noark.codec.along.data.SkillFire_S2C_Msg;

import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.TimeUnit;

/**
 * @author Allen Jiang
 */
@Fork(1)
@Warmup(iterations = 10, time = 1)
@Measurement(iterations = 10, time = 1)
@OutputTimeUnit(TimeUnit.SECONDS)
@BenchmarkMode({Mode.Throughput})
@State(Scope.Benchmark)
public class ProtoSerializeBenchMark {
    private SkillFire_S2C_Msg skillFire_s2C_msg = DataUtil.build(SkillFire_S2C_Msg.class);
    private Fury fury;

    private Fury furyX;

    private RuntimeSchema<SkillFire_S2C_Msg> schema;

    @Setup
    public void init() throws IOException {
        JSONFactory.setDisableAutoType(true);
        JSONFactory.setDisableReferenceDetect(true);
        ClassLoader classLoader = ProtoSerializeBenchMark.class.getClassLoader();
        try (InputStream is = classLoader.getResourceAsStream("along/message.txt")) {
            skillFire_s2C_msg = JSON.parseObject(is, SkillFire_S2C_Msg.class);
        }

        fury = Fury.builder().withLanguage(Language.JAVA)
                .withRefTracking(true).requireClassRegistration(false).build();

        furyX = Fury.builder().withLanguage(Language.JAVA)
                .withRefTracking(false).requireClassRegistration(true).withNumberCompressed(true).build();
        furyX.register(SkillFire_S2C_Msg.class);
        furyX.register(SkillCategory.class);
        furyX.register(HarmDTO.class);
        // String size = RamUsageEstimator.humanReadableUnits(RamUsageEstimator.sizeOf(skillFire_s2C_msg));


        this.schema = RuntimeSchema.createFrom((Class<SkillFire_S2C_Msg>) skillFire_s2C_msg.getClass());
    }

    @Benchmark
    public byte[] furySerialize() {
        return fury.serialize(skillFire_s2C_msg);
    }

    @Benchmark
    public byte[] furySerializeWithClassRegistrationAndNumberCompressed() {
        return furyX.serialize(skillFire_s2C_msg);
    }

    @Benchmark
    public byte[] jsonSerialize() {
        return JSONB.toBytes(skillFire_s2C_msg);
    }


    @Benchmark
    public byte[] jsonSerializeWithBeanToArray() {
        return JSONB.toBytes(skillFire_s2C_msg, JSONWriter.Feature.BeanToArray);
    }

    @Benchmark
    public byte[] jsonSerializeWithBeanToArrayAndFieldBase() {
        return JSONB.toBytes(skillFire_s2C_msg, JSONWriter.Feature.BeanToArray, JSONWriter.Feature.FieldBased);
    }

    @Benchmark
    public byte[] protostuffSerialize() {
        return SerializingUtil.serialize(skillFire_s2C_msg, schema);
    }


    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder()
                .include(ProtoSerializeBenchMark.class.getSimpleName()).result("result.json")
                .resultFormat(ResultFormatType.JSON)
                //      .addProfiler(GCProfiler.class)
//                .addProfiler(CompilerProfiler.class)
//                .verbosity(VerboseMode.EXTRA)
                // .addProfiler(JavaFlightRecorderProfiler.class)
                .build();
        new Runner(opt).run();
    }
}
